/*
 * Copyright (c) 2025 Huawei Device, Inc. Ltd. and <马弓手>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "CDVBroadcaster.h"
#include "CordovaViewController.h"
#include "cJSON.h"
#include "hiappevent/hiappevent.h"
#include <hiappevent/hiappevent_param.h>
REGISTER_PLUGIN_CLASS(CDVBroadcaster)

const string CDVBroadcaster::const_watcher_name = "harmony.cordova";
const string CDVBroadcaster::const_default_domain = "harmonyCordova";
bool CDVBroadcaster::execute(const string&  action, cJSON* args, CallbackContext cbc)
{
    if(action == "fireNativeEvent") {
        string strEventName;
        if(cJSON_GetArraySize(args) > 0) {
            strEventName = cJSON_GetArrayItem(args, 0)->valuestring;
        }
        string strParas;
        if(cJSON_GetArraySize(args) > 1) {
            cJSON* pJson = cJSON_GetArrayItem(args, 1);
            if(pJson->type == cJSON_String) {
                strParas = pJson->valuestring;
            }
            if(pJson->type == cJSON_Object || pJson->type == cJSON_Array) {
                char* pStr = cJSON_Print(pJson);
                strParas = pStr;
                free(pStr);
            }
            
            if(pJson->type == cJSON_Number) {
                char  szBuf[256];
                sprintf(szBuf, "%f", pJson->valuedouble);
                strParas = szBuf;
            }
            
            if(pJson->type == cJSON_True) {
                strParas = "true";
            }
            
            if(pJson->type == cJSON_False) {
                strParas = "false";
            }
            WriteAppEvent(strParas, strEventName);
            cbc.success();
        }
    }
    
    if(action == "addEventListener") {
        string strEventName;
        if(cJSON_GetArraySize(args) > 0) {
            strEventName = cJSON_GetArrayItem(args, 0)->valuestring;
        }
        
        if(strEventName.empty()) {
            cbc.error("event name null or empty");
            return true;
        }
        if(m_mapEventNameToWatcher.find(strEventName) != m_mapEventNameToWatcher.end()) {
            cbc.error("The event has been observed");
            return true;
        }
        HiAppEvent_Watcher* pWatcher = RegisterWatcher(strEventName);
        m_mapEventNameToWatcher[strEventName] = pWatcher;
        cbc.success();
    }
    
    if(action == "removeEventListener") {
         string strEventName;
        if(cJSON_GetArraySize(args) > 0) {
            strEventName = cJSON_GetArrayItem(args, 0)->valuestring;
        }
        
        if(strEventName.empty()) {
            cbc.error("event name null or empty");
            return true;
        }
        if(m_mapEventNameToWatcher.find(strEventName) != m_mapEventNameToWatcher.end()) {
            OH_HiAppEvent_RemoveWatcher(m_mapEventNameToWatcher[strEventName]);
            m_mapEventNameToWatcher.erase(strEventName);
        }
        cbc.success();
    }
    return true;
}

map<string, HiAppEvent_Watcher*> & CDVBroadcaster::getEventName()
{
    return  m_mapEventNameToWatcher;
}

bool CDVBroadcaster::fireEvent(const string& strEventName, const string& strEventData)
{
    string strJsCode = "window.broadcaster.fireEvent('";
    strJsCode += strEventName + "'," ;
    cJSON* pJson = cJSON_Parse(strEventData.c_str());
    if(pJson != NULL) {
        cJSON* pParams = cJSON_GetObjectItem(pJson, "params");
        char* pRetSting = cJSON_Print(pParams);
        cJSON* pJsonParams = cJSON_Parse(pRetSting);
        if(pJsonParams != nullptr) {
            cJSON_Delete(pJsonParams);
            strJsCode += pRetSting;
            strJsCode += ")";
        } else {
            strJsCode += "'";
            strJsCode += pRetSting;
            strJsCode += "')";
        }
        free(pRetSting);
        cJSON_Delete(pJson);
    } else {
        strJsCode += "'";
        strJsCode += strEventData;
        strJsCode += "')";
    }
    
    vector<string> vecWebTag = Application::getAllWebTag();
    for(int i=0; i<vecWebTag.size(); i++) {
        ((CordovaViewController*)Application::g_cordovaViewController)->addJavaScript(vecWebTag[i], strJsCode);
    }
    return true;
}
HiAppEvent_Watcher* CDVBroadcaster::RegisterWatcher(const string& strEventName)
{
    // 开发者自定义观察者名称，系统根据不同的名称来识别不同的观察者。
    HiAppEvent_Watcher* appEventWatcher = OH_HiAppEvent_CreateWatcher(CDVBroadcaster::const_watcher_name.c_str());
    // 设置订阅的事件名称为click。
    const char *names[] = {strEventName.c_str()};
    // 开发者订阅感兴趣的应用事件，此处订阅了button相关事件。
    OH_HiAppEvent_SetAppEventFilter(appEventWatcher, const_default_domain.c_str(), 0, names, 1);
    // 开发者设置已实现的回调函数，观察者接收到事件后回立即触发OnReceive回调。
    OH_HiAppEvent_SetWatcherOnReceive(appEventWatcher, CDVBroadcaster::OnReceive);
    // 使观察者开始监听订阅的事件。
    OH_HiAppEvent_AddWatcher(appEventWatcher);
    return appEventWatcher;
}

void CDVBroadcaster::OnReceive(const char *domain, const struct HiAppEvent_AppEventGroup *appEventGroups, uint32_t groupLen)
{
    CDVBroadcaster* pBroadcaster = (CDVBroadcaster*)((CordovaViewController*)Application::g_cordovaViewController)->getPluginManager()->getPlugin("broadcaster");
    map<string, HiAppEvent_Watcher*>& mapEventName = pBroadcaster->getEventName();
    for (int i = 0; i < groupLen; ++i) {
        for (int j = 0; j < appEventGroups[i].infoLen; ++j) {
            if(mapEventName.find(appEventGroups[i].appEventInfos[j].name) != mapEventName.end()) {
                pBroadcaster->fireEvent(appEventGroups[i].appEventInfos[j].name, appEventGroups[i].appEventInfos[j].params);
            }
        }
    }
}

bool CDVBroadcaster::WriteAppEvent(const string& strParams, const string& strEventName)
{
    auto params = OH_HiAppEvent_CreateParamList();
    OH_HiAppEvent_AddStringParam(params, "params", strParams.c_str());
    OH_HiAppEvent_Write(const_default_domain.c_str(), strEventName.c_str(), EventType::BEHAVIOR, params);
    OH_HiAppEvent_DestroyParamList(params);
    return true;
}